Using ws.geonames.org timezone webservice without WSDL

The great site geonames.org offers some webservices. One of these is called timezone and will give you the timezone information for a given geographic Latitude and Longitude. With a GPS enabled Windows Mobile device you can so query the webservice and get timezone informations for the current location.

Unfortunately the webservice does not offer WSDL and so you have to write your own wrapper class. I wrote a small class that does the HttpWebRequest and decodes the xml reponse for easy use in your application.

When you query the webservice you have to use the following form:

http://ws.geonames.org/timezone?lat=47.01&lng=10.2

The answer will then be:

         <geonames>
            <timezone>
                <countryCode>AT</countryCode>
                <countryName>Austria</countryName>
                <lat>47.01</lat>
                <lng>10.2</lng>
                <timezoneId>Europe/Vienna</timezoneId>
                <dstOffset>2.0</dstOffset>
                <gmtOffset>1.0</gmtOffset>
                <rawOffset>1.0</rawOffset>
                <time>2010-03-02 12:14</time>
            </timezone>
        </geonames>

The sample application will use this answer and shows the result:

The attached Visual Studio 2005 SmartDevice Windows Mobile 5 project has two classes: GeonamesTZ is a blocking class and geonamesTZ is a non-blocking class.

A blocking code is never good and so I implemented the class with an event. To use the class include a using statement for the geonames namespace and then initialize a new geonamesTZ object. Then add an event handler for the object’s event delegate. Finally add a event handler function that will get the timezone data.

using geonames;
    public partial class Form1 : Form
    {
        geonamesTZ myGeoTZ;
        geonamesTZfields tzFields;

        public Form1()
        {
            InitializeComponent();
            myGeoTZ = new geonamesTZ();
            myGeoTZ.geonamesEventHandler += new geonamesEvent(myGeoTZ_geonamesEvent);
        }

        void myGeoTZ_geonamesEvent(object sender, geonamesEventArgs e)
        {
            //Cursor.Current = Cursors.Default;
            //MessageBox.Show(e.m_myEventArgumentdata.strCountryName);
            tzFields = e.m_myEventArgumentdata;
            updateUI(tzFields);
        }

To get the timezone information you have to call the class function

            myGeoTZ.startRequest(txtLat.Text, txtLng.Text); //the answer should come...

and after some time the function myGeoTZ_geonamesEvent is called with the received data. Using the event driven approach will give the possibility to write an app that reads GPS data and updates timezone information in the background. Maybe this is usefull for Windows Mobile phones that often ’fly’ between continents. The timezone can then be updated in background.

As updating the UI from a different thread may fail, the updateUI function is implemented like this:

        // see http://blog.opennetcf.com/ctacke/2008/12/03/ControlInvokeWithoutExplicitDelegates.aspx
        public void updateUI(geonames.geonamesTZfields myTZinfos)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new EventHandler(delegate(object o, EventArgs a)
                {
                    txtDSToffset.Text = myTZinfos.dstOffset.ToString();
                    txtGMToffset.Text = myTZinfos.gmtOffset.ToString();
                    txtTimzoneID.Text = myTZinfos.strTimezoneID;
                    txtTime.Text = myTZinfos.tzTime.ToString();
                    txtTZcountryCode.Text = myTZinfos.strCountryCode;
                    lblStatus.Text = myTZinfos.strLastError;
                }));
            }
            else
            {
                txtDSToffset.Text = myTZinfos.dstOffset.ToString();
                txtGMToffset.Text = myTZinfos.gmtOffset.ToString();
                txtTimzoneID.Text = myTZinfos.strTimezoneID;
                txtTime.Text = myTZinfos.tzTime.ToString();
                txtTZcountryCode.Text = myTZinfos.strCountryCode;
                lblStatus.Text = myTZinfos.strLastError;
            }
        }

That was much easier than doing invokes on every txt field separately.

The raw xml response is parsed by another class called xml_helper. This class makes it easy to extract single values from the raw xml response. Here is one example:

namespace xml_helper{
    public static class xml_helper
    {
        public static string getStrSetting(StringBuilder sb, String sField)
        {
            string sVal = sb.ToString();
            string sFieldString = "<" + sField + ">";
            int iIdx = sVal.IndexOf(sFieldString);
            string sResult = "";
            if (iIdx >= 0)
            {
                string fieldStr = sVal.Substring(iIdx);
                string ssidStr = fieldStr.Remove(0, sFieldString.Length);
                iIdx = ssidStr.IndexOf("</");
                sResult = ssidStr.Substring(0, iIdx);
            }
            return sResult;
        }

These simple functions can be easily used on a xml file with single element data. Just call, for example getStrSettings with the xml string and the setting you would like to get.

Download Visual Studio 2005 SmartDevice Windows Mobile 5 project: [Download not found]